#include "./fonttypetest.h"
#include <string>

FontTypeTest::FontTypeTest()
{
    m_hDC = ::CreateCompatibleDC(NULL);
    ::SetGraphicsMode(m_hDC, GM_ADVANCED);
    ::SetBkMode(m_hDC, TRANSPARENT);
}

FontTypeTest::~FontTypeTest()
{
    ::DeleteDC(m_hDC);
}

FontTypeTest::FontBuffer FontTypeTest::GetGlyphBuffer(wchar_t c, FontMode mode)
{
    FontBuffer fontBuffer = {0};
    switch (mode)
    {
    case FontMode_Nothing:
        {
            GlyphInfo glyphInfo = {0};

            HFONT ft = GetDefaultFont(NONANTIALIASED_QUALITY);
            ::SelectObject(m_hDC, ft);
            MeasureText(c, glyphInfo);

            char* bits = new char[glyphInfo.width * glyphInfo.height * 4];
            TextOutToBuffer(ft, bits, glyphInfo);

            fontBuffer.width = glyphInfo.width;
            fontBuffer.height = glyphInfo.height;
            fontBuffer.buf = MaskToA8((unsigned int*)bits, glyphInfo);
            delete[] bits;
        }
        break;
    case FontMode_ClearType:
        {
            GlyphInfo glyphInfo = {0};

            HFONT ft = GetDefaultFont(CLEARTYPE_QUALITY);
            ::SelectObject(m_hDC, ft);
            MeasureText(c, glyphInfo);

            char* bits = new char[glyphInfo.width * glyphInfo.height * 4];
            TextOutToBuffer(ft, bits, glyphInfo);

            fontBuffer.width = glyphInfo.width;
            fontBuffer.height = glyphInfo.height;
            fontBuffer.buf = MaskToClearType((unsigned int*)bits, glyphInfo);
            delete[] bits;
        }
        break;
    case FontMode_ClearType2:
        {
            GlyphInfo glyphInfo = {0};

            HFONT ft = GetDefaultFont(CLEARTYPE_QUALITY);
            ::SelectObject(m_hDC, ft);
            MeasureText(c, glyphInfo);

            char* bits = new char[glyphInfo.width * glyphInfo.height * 4];
            TextOutToBuffer(ft, bits, glyphInfo);

            fontBuffer.width = glyphInfo.width;
            fontBuffer.height = glyphInfo.height;
            fontBuffer.buf = MaskToClearType((unsigned int*)bits, glyphInfo);
            delete[] bits;

            //set all white color to transparent
            unsigned int* src = (unsigned int*)fontBuffer.buf;
            for (int y = 0; y < glyphInfo.height; ++y)
            {
                for (int i = 0; i < glyphInfo.width; ++i)
                {
                    if (*src == 0xffffffff)
                    {
                        //white color
                        *src = 0;
                    }
                    src++;
                }
            }
        }
        break;
    case FontMode_Antialias:
        {
            GlyphInfo glyphInfo = {0};

            HFONT ft = GetDefaultFont(CLEARTYPE_QUALITY);
            ::SelectObject(m_hDC, ft);
            MeasureText(c, glyphInfo);

            char* bits = new char[glyphInfo.width * glyphInfo.height * 4];
            TextOutToBuffer(ft, bits, glyphInfo);

            fontBuffer.width = glyphInfo.width;
            fontBuffer.height = glyphInfo.height;
            fontBuffer.buf = MaskToA8((unsigned int*)bits, glyphInfo);
            delete[] bits;
        }
        break;
    }
    return fontBuffer;
}

HFONT FontTypeTest::GetDefaultFont(int quality)
{
    LOGFONT lf = {0};
    lf.lfWeight = FW_NORMAL;
    lf.lfItalic = 0;
    lf.lfHeight = -64;
    lf.lfQuality = quality;
    lf.lfCharSet = DEFAULT_CHARSET;
    wsprintf(lf.lfFaceName, L"Arial");

    return CreateFontIndirect(&lf);
}

void FontTypeTest::MeasureText(wchar_t c, GlyphInfo& glyphInfo)
{
    GLYPHMETRICS gm = {0};
    MAT2 fMat22 = {0};
    fMat22.eM11 = FixedFromDouble(0.25);
    fMat22.eM12 = FixedFromDouble(0);
    fMat22.eM21 = FixedFromDouble(0);
    fMat22.eM22 = FixedFromDouble(-0.25);

    WORD index = 0;
    WCHAR uchar[2];
    uchar[0] = c;

    ::GetGlyphIndices(m_hDC, uchar, 1, &index, 0);
    ::GetGlyphOutline(m_hDC, index, GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);

    glyphInfo.glyphID = index;
    glyphInfo.left = gm.gmptGlyphOrigin.x - 2;
    glyphInfo.top = gm.gmptGlyphOrigin.y - gm.gmBlackBoxY - 2;
    glyphInfo.width = gm.gmBlackBoxX + 4;
    glyphInfo.height = gm.gmBlackBoxY + 4;
}
  
void FontTypeTest::TextOutToBuffer(HFONT hFont, char* buf, GlyphInfo glyphInfo)
{
    HDC hDC = ::CreateCompatibleDC(NULL);
    ::SetGraphicsMode(hDC, GM_ADVANCED);
    ::SetBkMode(hDC, TRANSPARENT);
    ::SetTextAlign(hDC, TA_LEFT | TA_BASELINE);
    ::SelectObject(hDC, hFont);
    ::SetTextColor(hDC, RGB(0, 0, 0));

    BITMAPINFO info = {0};
    info.bmiHeader.biSize = sizeof(info.bmiHeader);
    info.bmiHeader.biWidth = glyphInfo.width;
    info.bmiHeader.biHeight = glyphInfo.height;
    info.bmiHeader.biPlanes = 1;
    info.bmiHeader.biBitCount = 32;
    info.bmiHeader.biCompression = BI_RGB;
    HBITMAP hBitmap;
    void* bits;
    hBitmap = ::CreateDIBSection(hDC, &info, DIB_RGB_COLORS, &bits, 0, 0);
    ::SelectObject(hDC, hBitmap);

    memset(bits, 0xFF, glyphInfo.width * glyphInfo.height * 4);

    //set all white color to transparent
    //unsigned int* src = (unsigned int*)bits;
    //for (int y = 0; y < glyphInfo.height; ++y)
    //{
    //    for (int i = 0; i < glyphInfo.width; ++i)
    //    {
    //        //white color
    //        *src = 0xff000000;
    //        src++;
    //    }
    //}

    XFORM xform = {0};
    xform.eM11 = 0.25;
    xform.eM12 = 0;
    xform.eM21 = 0;
    xform.eM22 = 0.25;
    xform.eDx = (float)-glyphInfo.left;
    xform.eDy = (float)-glyphInfo.top;
    ::SetWorldTransform(hDC, &xform);

    ::ExtTextOut(hDC, 0, 0, ETO_GLYPH_INDEX, NULL, (LPCWSTR)&glyphInfo.glyphID, 1, NULL);
    ::GdiFlush();

    memcpy(buf, bits, glyphInfo.width * glyphInfo.height * 4);

    ::DeleteDC(hDC);
    ::DeleteObject(hBitmap);
    ::DeleteObject(hFont);
}

void* FontTypeTest::MaskToA8(unsigned int* src, GlyphInfo glyphInfo)
{
    char* dstBits = new char[glyphInfo.width * glyphInfo.height];

    char* dst = dstBits + glyphInfo.width * (glyphInfo.height - 1);

    for (int y = 0; y < glyphInfo.height; ++y)
    {
        for (int i = 0; i < glyphInfo.width; ++i)
        {
            dst[i] = RGBToA8(src[i] ^ -1);
        }
        src += glyphInfo.width;
        dst -= glyphInfo.width;
    }

    void* maskBits = MaskBlack(dstBits, glyphInfo);
    delete dstBits;

    return maskBits;
}

char FontTypeTest::RGBToA8(unsigned int rgb)
{
    int r = (rgb >> 16) & 0xFF;
    int g = (rgb >> 8)  & 0xFF;
    int b = (rgb >> 0)  & 0xFF;

    int ave = (r * 2 + g * 5 + b) >> 3;
    return ave;
}

void* FontTypeTest::MaskBlack(char* mask, GlyphInfo glyphInfo)
{
    unsigned int* device = new unsigned int[glyphInfo.width * glyphInfo.height * 4];
    unsigned int* dst = device;
    memset(dst, 0, glyphInfo.width * glyphInfo.height * 4);
    int width = glyphInfo.width;
    int height = glyphInfo.height;

    do
    {
        int w = width;
        do 
        {
            unsigned aa = *mask++;
            *dst = (aa << 24) + AlphaMulQ(*dst, (255 - aa) + 1);
            dst += 1;
        } while (--w != 0);
    } while (--height != 0);

    return device;
}

void* FontTypeTest::MaskToClearType(unsigned int* src, GlyphInfo glyphInfo)
{
    unsigned int* dstBits = new unsigned int[glyphInfo.width * glyphInfo.height];
    unsigned int* dst = dstBits + glyphInfo.width * (glyphInfo.height - 1);

    for (int y = 0; y < glyphInfo.height; ++y)
    {
        for (int i = 0; i < glyphInfo.width; ++i)
        {
            dst[i] = src[i];
            dst[i] |= 0xff000000;
        }
        src += glyphInfo.width;
        dst -= glyphInfo.width;
    }
    return dstBits;
}

FIXED FontTypeTest::FixedFromDouble(double d)
{
    long l;
    l = (long) (d * 65536L);
    return *(FIXED *)&l;
}

unsigned int FontTypeTest::AlphaMulQ(unsigned int c, unsigned scale)
{
    unsigned int mask = 0x00ff00ff;

    unsigned int rb = ((c & mask) * scale) >> 8;
    unsigned int ag = ((c >> 8) & mask) * scale;
    return (rb & mask) | (ag & ~mask);
}